package com.liruo.treasureattic.service.url.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liruo.treasureattic.common.httpmsg.exception.CommonResultException;
import com.liruo.treasureattic.common.httpmsg.exception.ValidatorException;
import com.liruo.treasureattic.common.httpmsg.result.Errno;
import com.liruo.treasureattic.common.model.dto.url.UrlFolderAddReq;
import com.liruo.treasureattic.common.model.dto.url.UrlFolderUpdateReq;
import com.liruo.treasureattic.common.model.entity.TreasureUrl;
import com.liruo.treasureattic.common.model.entity.UrlFolder;
import com.liruo.treasureattic.common.model.other.Pair;
import com.liruo.treasureattic.common.model.vo.url.UrlFolderVo;
import com.liruo.treasureattic.common.util.ValidatorUtil;
import com.liruo.treasureattic.service.url.mapper.TreasureUrlMapper;
import com.liruo.treasureattic.service.url.mapper.UrlFolderMapper;
import com.liruo.treasureattic.service.url.model.UrlConstant;
import com.liruo.treasureattic.service.url.service.UrlFolderService;
import com.liruo.treasureattic.starter.jwtlogin.holder.UserLoginStatusHolder;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
* @author LYM
* @description 针对表【url_folder】的数据库操作Service实现
* @createDate 2022-10-31 20:29:39
*/
@Service
public class UrlFolderServiceImpl extends ServiceImpl<UrlFolderMapper, UrlFolder>
    implements UrlFolderService {
    @Resource
    private UrlFolderMapper urlFolderMapper;
    @Resource
    private TreasureUrlMapper treasureUrlMapper;
    @Override
    public long addDefaultFolder(UrlFolderAddReq folderAddReq) {
        //检查参数
        ValidatorUtil.anyNull(folderAddReq);

        if(folderAddReq.getFid() != null) {
            throw new CommonResultException(Errno.PARAMS, "default folder not need fid");
        }
        folderAddReq.setFid(UrlConstant.DEFAULT_FOLDER_FID);

        String defaultFolderName = folderAddReq.getName();
        ValidatorUtil.anyNull(new String[]{defaultFolderName},
                "defaultFolderName  or category is null"
        );

        ValidatorUtil.checkCommonName(defaultFolderName,
                UrlConstant.MIN_URL_FOLDER_NAME,
                UrlConstant.MAX_URL_FOLDER_NAME
        );


        //没有默认folder
        Long userId = UserLoginStatusHolder.getLoginStatus().getUserId();
        UrlFolder defaultFolder = this.getOne(new QueryWrapper<UrlFolder>()
                .eq("uid", userId)
                .eq("fid", UrlConstant.DEFAULT_FOLDER_FID));
        if(defaultFolder != null) {
            throw new ValidatorException(Errno.ALREADY_EXISTS, "user already has default folder");
        }

        //add
        UrlFolder urlFolder = new UrlFolder();
        BeanUtils.copyProperties(folderAddReq, urlFolder);
        urlFolder.setUid(userId);
        this.save(urlFolder);
        return urlFolder.getId();
    }
    @Override
    public long addFolder(UrlFolderAddReq folderAddReq) {
        //检查参数
        addFolderCheck(folderAddReq);

        Long userId = UserLoginStatusHolder.getLoginStatus().getUserId();
        //add
        UrlFolder urlFolder = new UrlFolder();
        BeanUtils.copyProperties(folderAddReq, urlFolder);
        urlFolder.setUid(userId);
        ValidatorUtil.dbOpt(this.save(urlFolder),"fail when save urlFolder");
        return urlFolder.getId();
    }

    public void addFolderCheck(UrlFolderAddReq folderAddReq) {
        ValidatorUtil.anyNull(folderAddReq);

        Long fid = folderAddReq.getFid();
        if(fid == null) {
            throw new ValidatorException(Errno.PARAMS, "folder need fid");
        }else if(fid.equals(UrlConstant.DEFAULT_FOLDER_FID)){
            throw new ValidatorException(Errno.PARAMS, "can add default folder again");
        }

        String folderName = folderAddReq.getName();
        ValidatorUtil.anyNull(new String[]{folderName},
                "folderName  or category is null"
        );

        ValidatorUtil.checkCommonName(folderName,
                UrlConstant.MIN_URL_FOLDER_NAME,
                UrlConstant.MAX_URL_FOLDER_NAME
        );

        //有father folder
        Long userId = UserLoginStatusHolder.getLoginStatus().getUserId();
        UrlFolder father = this.getOne(new QueryWrapper<UrlFolder>()
                .select("id")
                .eq("uid", userId)
                .eq("id", fid)
        );
        if(father == null) {
            throw new ValidatorException(Errno.NOT_EXISTS, "user has not father folder");
        }
    }



    @Override
    public void updateFolder(UrlFolderUpdateReq urlFolderUpdateReq) {
        List<UrlFolderUpdateReq> urlFolderUpdateReqs = updateFolderCheck(Collections.singletonList(urlFolderUpdateReq));
        if (urlFolderUpdateReqs.isEmpty()) return;

        UrlFolder updateFolder = new UrlFolder();
        BeanUtils.copyProperties(urlFolderUpdateReqs.get(0), updateFolder);
        this.updateById(updateFolder);
    }

    /**
     * 先进行noDB检查，不与旧数据对比
     * @return 需要更新的folder
     */
    private List<UrlFolderUpdateReq> updateFolderCheck(List<UrlFolderUpdateReq> urlFolderUpdateReqs) {
        //检查参数
        ValidatorUtil.anyCollectionEmpty("can't update null folder",urlFolderUpdateReqs);

        ArrayList<UrlFolderUpdateReq> needUpdateFolders = new ArrayList<>(urlFolderUpdateReqs.size());
        urlFolderUpdateReqs.forEach(urlFolderUpdateReq -> {
            Long folderId = urlFolderUpdateReq.getId();
            ValidatorUtil.requireGreat0(folderId);

            String updateName = urlFolderUpdateReq.getName();
            ValidatorUtil.allNull(new String[]{updateName}, "update  folder can't all null",
                    urlFolderUpdateReq.getOpen(), urlFolderUpdateReq.getFid());

            if(StringUtils.isEmpty(updateName)){
                ValidatorUtil.checkCommonName(updateName,
                        UrlConstant.MIN_URL_FOLDER_NAME,
                        UrlConstant.MAX_URL_NAME);
            }
            needUpdateFolders.add(urlFolderUpdateReq);
        });
        Long userId = UserLoginStatusHolder.getLoginStatus().getUserId();

        Set<Long> folderIds = urlFolderUpdateReqs.stream()
                .map(UrlFolderUpdateReq::getId)
                .collect(Collectors.toSet());

        List<Long> fids = urlFolderUpdateReqs.stream()
                .filter(req -> req.getFid() != null)
                .peek(req -> {
                    if (req.getFid() == UrlConstant.DEFAULT_FOLDER_FID) {
                        throw new ValidatorException(Errno.PARAMS, "can' t add to default folder url==" + req.getName() + ": " + req.getName());
                    }
                })
                .map(UrlFolderUpdateReq::getFid)
                .toList();
        ValidatorUtil.requireGreat0(fids);
        folderIds.addAll(fids);

        if(!this.isAllExists(folderIds, userId)){
            throw new ValidatorException(Errno.PARAMS, "some father folder not exists");
        }

        return needUpdateFolders;
    }

    /**
     * 判断一个folder是否存在
     */
    public boolean exists(Long id, Long userId) {
        UrlFolder newFatherFolder = this.getOne(new QueryWrapper<UrlFolder>()
                .select("id")
                .eq("uid", userId)
                .eq("id", id)
        );
        return newFatherFolder != null;
    }

    @Override
    public List<UrlFolderVo> getAllFolder() {
        Long userId = UserLoginStatusHolder.getLoginStatus().getUserId();
        return list(new QueryWrapper<UrlFolder>()
                .eq("uid", userId))
                .stream()
                .map(folder->{
                    UrlFolderVo urlFolderVo = new UrlFolderVo();
                    BeanUtils.copyProperties(folder, urlFolderVo);
                    return urlFolderVo;
                })
                .toList();
    }

    //   非Controller方法
    /**
     * 检查url的father存在并且不是默认folder
     */
    public void folderExistAndNotDefault(Set<Long> ids){
        List<UrlFolder> folders = this.list(new QueryWrapper<UrlFolder>()
                .select("fid")
                .in("id", ids)
        );
        if(folders.size() != ids.size()){
            throw new ValidatorException(Errno.PARAMS, "some folder not exists");
        }

        boolean hasDefaultFolder = folders.stream()
                .anyMatch(f -> UrlConstant.DEFAULT_FOLDER_FID == f.getFid());
        if(hasDefaultFolder){
            throw new ValidatorException(Errno.PARAMS, "can't add url to default folder");
        }
    }

    /**
     * 不存在抛出异常
     * @return 默认folder
     */
    @Override
    public UrlFolder getDefaultFolder(long uid) {
        return  this.getOne(
                new QueryWrapper<UrlFolder>()
                        .eq("fid", UrlConstant.DEFAULT_FOLDER_FID)
                        .eq("uid",uid)
        );
    }

    /**
     * 默认folder不存在抛出异常
     * @return 默认folder的id
     */
    @Override
    public long getDefaultFolderId(long uid) {
        UrlFolder defaultFolder = this.getOne(
                new QueryWrapper<UrlFolder>()
                        .select("id")
                        .eq("fid", UrlConstant.DEFAULT_FOLDER_FID)
                        .eq("uid", uid)
        );
        if(defaultFolder == null){
            throw new CommonResultException(Errno.NOT_EXISTS, "default folder not exists but build when user register");
        }
        return defaultFolder.getId();
    }
    @Override
    public List<UrlFolder> getSubFolder(long fid, boolean open, long uid){
        return list(new QueryWrapper<UrlFolder>()
                .eq("uid", uid)
                .eq("fid", fid)
                .eq(open, "open", open)
        );
    }
    public List<Long> getSubFolderIdsByFids(Collection<Long> fids, long uid){
        return list(new QueryWrapper<UrlFolder>()
                .select("id")
                .eq("uid", uid)
                .in("fid", fids)
        ).stream().map(UrlFolder::getId).toList();
    }

    @Override
    public Pair<Integer, Integer> realDeleteUrlFolder(int delValCycleDays) {
        List<Long> realDeleteFolderIds = urlFolderMapper.getBeDeletedFolderId(delValCycleDays);
        List<Long> realDeleteUrlIds = treasureUrlMapper.selectList(new QueryWrapper<TreasureUrl>()
                .select("id")
                .eq("deleted", true)
                .in("fid", realDeleteFolderIds)
        ).stream().map(TreasureUrl::getId).toList();
        int deleteFolderCnt = urlFolderMapper.delete(new QueryWrapper<UrlFolder>()
                .eq("deleted", true)
                .in("id", realDeleteFolderIds)
        );
        int deleteUrlCnt = treasureUrlMapper.delete(new QueryWrapper<TreasureUrl>()
                .eq("deleted", true)
                .in("id", realDeleteUrlIds)
        );
        return new Pair<>(deleteFolderCnt, deleteUrlCnt);
    }

    /**
     *
     * @param size  需要创建url的尺寸
     * @return 第一个id,下面的递增就好,最大 return + size - 1
     */
    public long occupyUrlFolderId(int size){
        Long userId = UserLoginStatusHolder.getLoginStatus().getUserId();
        ArrayList<UrlFolder> folders = new ArrayList<>(size);
        UrlFolder first = new UrlFolder();
        String firstName = "first";
        first.setName(firstName);
        first.setUid(userId);
        folders.add(first);
        UrlFolder folder;
        for (int i = 1; i < size; i++) {
            folder = new UrlFolder();
            folder.setUid(userId);
            folders.add(folder);
        }
        if(!saveBatch(folders)){
            return -1;
        }
        first = getOne(new QueryWrapper<UrlFolder>()
                .eq("uid", userId)
                .eq("name", firstName)
        );
        return first.getId();
    }

    public boolean isAllExists(Collection<Long> folderIds, Long userId){
        return count(new QueryWrapper<UrlFolder>()
                .select("id")
                .eq("uid", userId)
                .in("id", folderIds)) == folderIds.size();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchUpdateFolder(List<UrlFolderUpdateReq> batchUpdateFolderList) {
        ValidatorUtil.anyCollectionEmpty("can't batch update null folder",batchUpdateFolderList);

        this.updateFolderCheck(batchUpdateFolderList);

        List<UrlFolder> urlFolders = batchUpdateFolderList.stream()
                .map(folder -> {
                    UrlFolder urlFolder = new UrlFolder();
                    BeanUtils.copyProperties(folder, urlFolder);
                    return urlFolder;
                })
                .toList();
        ValidatorUtil.dbOpt(this.updateBatchById(urlFolders),
                "fail when batch update folder");
    }

    public List<UrlFolder> getLast(int size) {
        Long userId = UserLoginStatusHolder.getLoginStatus().getUserId();
        return this.list(new QueryWrapper<UrlFolder>()
                .eq("uid", userId)
                .orderByDesc("id")
                .last("limit " + size)
        );
    }
}




